//
//  BiliHotVideoActor.swift
//  RoBiliBiliHotVideo
//
//  Created by 罗万能 on 2025/7/6.
//

import Foundation
import SwiftData
import SwiftyJSON
@available(macOS 14, *)
@ModelActor public actor BiliHotVideoActor{
//    var videoDonloadTask:[PersistentIdentifier: VideoDownloadService] = [:]
//    var authorPicDonloadTask:[PersistentIdentifier: ImageDownloadService] = [:]
//    var videoDonloadTask:[PersistentIdentifier: VideoDownloadService] = [:]
//    var videoDonloadTask:[PersistentIdentifier: VideoDownloadService] = [:]
    
    enum PicDownloadType{
        case author
        case firstFrame
        case localTitle
    }
    let publicPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appending(component: "BiliHotVideo")
    
    final class VideoDownloadService: NSObject,URLSessionDownloadDelegate{
        func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
            Task{
                await self.actor.updateLocalVideoFinished(persistentId: persistentId, session, downloadTask: downloadTask, didFinishDownloadingTo: location)
            }
        }
        func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
            Task{
                await self.actor.updateLocalVideoProgress(persistentId: persistentId, session, downloadTask: downloadTask, didWriteData: bytesWritten, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite)
            }
        }
        
        let persistentId: PersistentIdentifier
        let actor: BiliHotVideoActor
        init(persistentId: PersistentIdentifier, actor: BiliHotVideoActor) {
            self.persistentId = persistentId
            self.actor = actor
        }
        
        
    }
    final class ImageDownloadService: NSObject,URLSessionDownloadDelegate{
        func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
            Task{
                await self.actor.updateLocalPicFinished(picType: self.picType, persistentId: persistentId, session, downloadTask: downloadTask, didFinishDownloadingTo: location)
            }
        }
        
        let picType: PicDownloadType
        let persistentId: PersistentIdentifier
        let actor: BiliHotVideoActor
        init(persistentId: PersistentIdentifier, actor: BiliHotVideoActor,picType: PicDownloadType) {
            self.persistentId = persistentId
            self.actor = actor
            self.picType = picType
        }
        
        
    }
 
    
    func startDownloadImages(model: BiliHotVideo){
        let persistentId = model.persistentModelID
        if let modelForDownload = self[persistentId,as: BiliHotVideo.self]{
            let config = URLSessionConfiguration.background(
                withIdentifier: "com.\(modelForDownload.bVid).backgroundSession"
            )
            config.isDiscretionary = true // 允许系统优化执行时机（如设备充电时）
            config.sessionSendsLaunchEvents = true // 任务完成后唤醒应用
            let session = URLSession(configuration: config)
            if  modelForDownload.localAuthorPic == nil || (modelForDownload.localAuthorPic != nil && !FileManager.default.fileExists(atPath: modelForDownload.localAuthorPic!.path)){
                let authorUrl = modelForDownload.parsingVideoItem.imgurl
                let    authDownloadTask = session.downloadTask(with: authorUrl)
                let authDownloadService = ImageDownloadService(persistentId: persistentId, actor: self, picType: .author)
                authDownloadTask.delegate = authDownloadService
                authDownloadTask.resume()
            }
            if  modelForDownload.localFirstFramePic == nil || (modelForDownload.localFirstFramePic != nil && !FileManager.default.fileExists(atPath: modelForDownload.localFirstFramePic!.path)){
                let firstFrameUrl = URL(string: modelForDownload.videoItem.first_frame)
                if let url = firstFrameUrl{
                    let urlRequest = URLRequest(url: url)
                   
                    let downloadTask = session.downloadTask(with: urlRequest)
                    let downloadService = ImageDownloadService(persistentId: persistentId, actor: self, picType: .firstFrame)
                    downloadTask.delegate = downloadService
                    downloadTask.resume()
                }
            }
            if  modelForDownload.localTitlePic == nil || (modelForDownload.localTitlePic != nil && !FileManager.default.fileExists(atPath: modelForDownload.localTitlePic!.path)){
                let localTitleUrl = URL(string: modelForDownload.videoItem.pic)
                if let url = localTitleUrl{
                    let urlRequest = URLRequest(url: url)
                   
                    let downloadTask = session.downloadTask(with: urlRequest)
                    let downloadService = ImageDownloadService(persistentId: persistentId, actor: self, picType: .localTitle)
                    downloadTask.delegate = downloadService
                    downloadTask.resume()
                }
            }
        }
    }
    func startDownloadVideo(model: BiliHotVideo){
        let persistentId = model.persistentModelID
        if let modelForDownload = self[persistentId,as: BiliHotVideo.self]{
            let config = URLSessionConfiguration.background(
                withIdentifier: "com.\(modelForDownload.bVid).backgroundSession"
            )
            config.isDiscretionary = true // 允许系统优化执行时机（如设备充电时）
            config.sessionSendsLaunchEvents = true // 任务完成后唤醒应用
            let session = URLSession(configuration: config)
//            session.delegateQueue = DispatchQueue(label: "\(modelForDownload.bVid)")
            if let localUrl = modelForDownload.localVideoUrl , FileManager.default.fileExists(atPath: localUrl.path) {
                return
            }
            guard let firstParsing = modelForDownload.parsingVideoItem.data.first else{
                return
            }
            let urlRequest = URLRequest(url: firstParsing.videoURL)
            var downloadTask: URLSessionDownloadTask
            if let resumData = modelForDownload.videoResumeData{
                downloadTask = session.downloadTask(withResumeData: resumData)
            }else{
                downloadTask = session.downloadTask(with: urlRequest)
            }
            let downloadService = VideoDownloadService(persistentId: persistentId, actor: self)
            downloadTask.delegate = downloadService
//            videoDonloadTask[persistentId] = downloadService
            downloadTask.resume()
        }
    }
    func createPublicPath(){
        do{
            try FileManager.default.createDirectory(at: publicPath, withIntermediateDirectories: true)
        }catch{
            fatalError("建立文件夹错误")
        }
    }
    func createBVidPath(bvid: String) -> URL{
        let filePath = publicPath.appending(component: bvid)
        do{
            try FileManager.default.createDirectory(at: filePath, withIntermediateDirectories: true)
            return filePath
        }catch{
            fatalError("建立文件夹错误")
        }
    }
    
    func saveModels(){
        do{
            try self.modelContext.save()
        }catch{
            print(error)
            fatalError("actor 保存model失败")
        }
    }
}

@available(macOS 14, *)
extension BiliHotVideoActor{
    
    func updateLocalVideoFinished(persistentId: PersistentIdentifier,_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL){
        do{
            if let modelForDownload = self[persistentId,as: BiliHotVideo.self]{
                let filePath = createBVidPath(bvid: modelForDownload.bVid)
                let fileName = downloadTask.response?.suggestedFilename ?? "\(modelForDownload.bVid).mp4"
                let fileUrl = filePath.appending(component: fileName)
                try FileManager.default.moveItem(at: location, to: fileUrl)
                modelForDownload.localVideoUrl = fileUrl
                modelForDownload.videoResumeData = nil
                modelForDownload.videoDownloadProgress = 1
                saveModels()
            }
        }catch{
            print(error)
        }
        
    }
    
    func updateLocalVideoProgress(persistentId: PersistentIdentifier,_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64){
        
            if let modelForDownload = self[persistentId,as: BiliHotVideo.self]{
                let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
                modelForDownload.videoDownloadProgress = progress
                saveModels()
            }
        
        
    }
    
    
    func updateLocalPicFinished(picType: PicDownloadType,persistentId: PersistentIdentifier,_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL){
        do{
            
            if let modelForDownload = self[persistentId,as: BiliHotVideo.self]{
                
                let filePath = createBVidPath(bvid: modelForDownload.bVid)
                var fileName: String = ""
                switch picType{
                    
                case .author:
                    fileName = downloadTask.response?.suggestedFilename ?? "\(modelForDownload.bVid)_author.jpg"
                    
                case .firstFrame:
                    fileName = downloadTask.response?.suggestedFilename ?? "\(modelForDownload.bVid)_firstFrame.jpg"
                case .localTitle:
                    fileName = downloadTask.response?.suggestedFilename ?? "\(modelForDownload.bVid)_localTitle.jpg"
                }
                let fileUrl = filePath.appending(component: fileName)
                try FileManager.default.moveItem(at: location, to: fileUrl)
                switch picType{
                    
                case .author:
                    modelForDownload.localAuthorPic = fileUrl
                    
                case .firstFrame:
                    modelForDownload.localFirstFramePic = fileUrl
                case .localTitle:
                    modelForDownload.localTitlePic = fileUrl
                }
                saveModels()
            }
        }catch{
            print(error)
        }
        
    }
    
//    func updateLocalPicProgress(persistentId: PersistentIdentifier,_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64){
//
//            if let modelForDownload = self[persistentId,as: BiliHotVideo.self]{
//                let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
//                modelForDownload.videoDownloadProgress = progress
//                saveModels()
//            }
//        
//        
//    }
}


//public func
@available(macOS 14, *)
extension BiliHotVideoActor{
    static private var shared: BiliHotVideoActor?
    static public func createShared(container: ModelContainer){
        if shared == nil {
            shared = BiliHotVideoActor(modelContainer: container)
        }
    }
    static public func getShared() -> BiliHotVideoActor{
        if shared == nil{
            let schema = Schema([
                BiliHotVideo.self
            ])
            let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)

            do {
                let container =  try ModelContainer(for: schema, configurations: [modelConfiguration])
                shared = BiliHotVideoActor(modelContainer: container)
            
            } catch {
                fatalError("Could not create ModelContainer: \(error)")
            }
        }
        return shared!
    }
    
    public func createBiliHotVideoModel(videoItem: VideoItem, parsingVideoItem: ParsingVideo){
        let descriptor = FetchDescriptor<BiliHotVideo>(predicate: #Predicate { $0.videoItem.bvid == videoItem.bvid })
        do{
            let result = try self.modelContext.fetch(descriptor)
            if result.count >= 1 {
                return
            }
        }catch{
            print(error)
            return
        }
        let model = BiliHotVideo(videoItem: videoItem, parsingVideoItem: parsingVideoItem)
        self.modelContext.insert(model)
        do{
            try self.modelContext.save()
            downloadBiliHotVideos(model: model)
            
        }catch{
            print(error)
            
        }
    }
    public func downloadBiliHotVideos(model: BiliHotVideo){
        startDownloadVideo(model: model)
        startDownloadImages(model: model)
    }
    public func fetchBiliHotVideosDebug() async {
//        searchHotJsonString.
        let data = searchHotJsonString.data(using: .utf8)!
       
        if let hostVideos = await SearchHotStore.requestSearchHotDebug(data: data)?.data?.list{
            var videoDic:[String: ParsingVideo] = [:]
            await withTaskGroup { group in
               
                for video in hostVideos{
                   
                    group.addTask{
                        let bvid = video.bvid
                       let pasing = await ParsingStore.parsing(bvid: video.bvid)
//                        if pasing != nil{
//                            videoDic[video.bvid] = pasing!
//                        }
//                        return pasing
                        return (bvid, pasing)
                       
                    }
                }
                
                for await result in group{
                    if result.1 != nil{
                        let bvid = result.0
                        videoDic[bvid] = result.1!
                    }
                }
            }
            for video in hostVideos{
                if let pasingVideo = videoDic[video.bvid]{
                    createBiliHotVideoModel(videoItem: video, parsingVideoItem: pasingVideo)
                }
            }
            
            
        }
    }
    public func fetchBiliHotVideos() async {
        if let hostVideos = await SearchHotStore.requestSearchHot()?.data?.list{
            var videoDic:[String: ParsingVideo] = [:]
            await withTaskGroup { group in
               
                for video in hostVideos{
                   
                    group.addTask{
                        let bvid = video.bvid
                       let pasing = await ParsingStore.parsing(bvid: video.bvid)
//                        if pasing != nil{
//                            videoDic[video.bvid] = pasing!
//                        }
//                        return pasing
                        return (bvid, pasing)
                       
                    }
                }
                
                for await result in group{
                    if result.1 != nil{
                        let bvid = result.0
                        videoDic[bvid] = result.1!
                    }
                }
            }
            for video in hostVideos{
                if let pasingVideo = videoDic[video.bvid]{
                    createBiliHotVideoModel(videoItem: video, parsingVideoItem: pasingVideo)
                }
            }
            
            
        }
    }
}
